package yu_liang

import (
	"device-modbus/core"
	"device-modbus/internal/biz/model"
	"encoding/binary"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/common/hlog"
)

// YuLiangSensorProtocol 聚碳雨量传感器协议实现（适配 ZTS-YL-*-3001-* 型号）
type YuLiangSensorProtocol struct{}

// NewProtocol 导出插件创建函数（签名严格匹配 core.ProtocolCreator）
// 插件管理器通过此函数生成雨量传感器协议实例
func NewProtocol() core.SensorProtocol {
	return &YuLiangSensorProtocol{}
}

// GetType 返回传感器类型（按手册定义为“雨量传感器”，类型标识为“yu_liang”）
func (p *YuLiangSensorProtocol) GetType() string {
	return "modbus"
}

// GetModel 返回传感器型号（手册中明确型号为 ZTS-YL-*-3001-*）
func (p *YuLiangSensorProtocol) GetModel() string {
	return "雨量"
}

// GetProtocolName 返回协议唯一标识（全局唯一，结合型号与协议类型定义）
func (p *YuLiangSensorProtocol) GetProtocolName() string {
	return "雨量"
}

// YuLiangSensorData 雨量传感器解析后的数据结构
// 字段定义严格遵循手册4.3节寄存器地址与4.4节数据解析规则
type YuLiangSensorData struct {
	DeviceAddr byte    // 设备地址（手册4.2节，1字节，默认0x01）
	FuncCode   byte    // 功能码（手册4.2节，1字节，如0x03查询/0x06配置）
	ValidBytes byte    // 有效字节数（手册4.2节，应答帧中标识数据区长度）
	Rainfall   float64 // 雨量值（单位：mm，手册4.3节：寄存器值扩大10倍，需÷10还原）
}

// BuildRequest 构建ModBus-RTU请求帧（严格遵循手册4.2节帧格式定义）
// 支持查询雨量值（0x03功能码）、修改设备地址（0x06功能码）、修改波特率（0x06功能码）
func (p *YuLiangSensorProtocol) BuildRequest(deviceAddr, funcCode byte, startAddr, dataLength uint16) ([]byte, error) {
	// 校验功能码合法性（手册4.3节支持0x03查询/0x06写配置）
	if funcCode != 0x03 && funcCode != 0x06 {
		return nil, fmt.Errorf("%w: 仅支持功能码0x03（查询）/0x06（配置），实际0x%02X",
			core.ErrorUnsupported, funcCode)
	}

	hlog.Debugf("构建雨量传感器请求帧 | 设备地址:0x%02X, 功能码:0x%02X, 起始寄存器:0x%04X, 数据长度:0x%04X",
		deviceAddr, funcCode, startAddr, dataLength)

	// 构建帧主体（手册4.2节：地址码1字节+功能码1字节+起始地址2字节+数据长度2字节）
	frame := make([]byte, 6)
	frame[0] = deviceAddr
	frame[1] = funcCode
	binary.BigEndian.PutUint16(frame[2:4], startAddr)
	binary.BigEndian.PutUint16(frame[4:6], dataLength)

	// 计算并添加CRC16校验（手册4.2节：CRC为16位冗余循环码，低位在前、高位在后）
	crc := calculateCRC(frame)
	frame = append(frame, byte(crc&0xFF))    // CRC低字节
	frame = append(frame, byte(crc>>8&0xFF)) // CRC高字节

	return frame, nil
}

// ParseResponse 解析ModBus-RTU应答帧（严格遵循手册4.4节示例与数据规则）
// 支持解析：查询雨量值（0x03功能码）、配置应答（0x06功能码） 有效字节数7个
func (p *YuLiangSensorProtocol) ParseResponse(resp []byte) (*model.SensorData, error) {
	// 校验设备地址（手册4.2节：默认0x01）
	if resp[0] != 0x01 {
		return nil, fmt.Errorf("%w: 设备地址应为0x01，实际0x%02X",
			core.ErrorInvalidResponse, resp[0])
	}
	// 1. 校验功能码与帧长度（适配实际返回7字节场景）
	switch resp[1] {
	case 0x03: // 查询雨量值应答（实际返回为7字节，修正预期长度）
		const expectedQueryLen = 7
		if len(resp) != expectedQueryLen {
			return nil, fmt.Errorf("%w: 查询雨量应答帧长度应为%d字节，实际%d字节",
				core.ErrorInvalidResponse, expectedQueryLen, len(resp))
		}

		// 校验有效字节数（手册4.4.1节：查询雨量应答有效字节数固定为0x02）
		if resp[2] != 0x02 {
			return nil, fmt.Errorf("%w: 查询雨量应答有效字节数应为0x02，实际0x%02X",
				core.ErrorInvalidResponse, resp[2])
		}

	default:
		return nil, fmt.Errorf("%w: 不支持的功能码0x%02X，仅支持0x03/0x06",
			core.ErrorUnsupported, resp[1])
	}

	// 2. CRC校验（手册4.2节：校验范围为除最后2字节外的所有数据）
	crcCheckData := resp[:len(resp)-2]
	expectedCRC := calculateCRC(crcCheckData)
	actualCRC := uint16(resp[len(resp)-2]) | uint16(resp[len(resp)-1])<<8
	if actualCRC != expectedCRC {
		return nil, fmt.Errorf("%w: CRC校验失败 | 预期0x%04X，实际0x%04X",
			core.ErrorInvalidResponse, expectedCRC, actualCRC)
	}

	// 3. 解析数据（区分查询/配置帧）
	var parsed *YuLiangSensorData
	switch resp[1] {
	case 0x03: // 解析查询雨量结果（手册4.4.1节：寄存器值扩大10倍，需÷10还原）
		rawRainfall := binary.BigEndian.Uint16(resp[3:5]) // 数据区为resp[3:5]（2字节）
		parsed = &YuLiangSensorData{
			DeviceAddr: resp[0], // 使用响应中的实际设备地址（支持配置后修改）
			FuncCode:   resp[1],
			ValidBytes: resp[2],
			Rainfall:   float64(rawRainfall) / 10.0, // 手册：雨量值 = 寄存器值 ÷ 10（单位mm）
		}
		hlog.Debugf("解析雨量数据成功 | 设备地址:0x%02X, 雨量值:%.1fmm, 原始寄存器值:0x%04X",
			parsed.DeviceAddr, parsed.Rainfall, rawRainfall)

	}

	// 4. 封装为全局统一的 SensorData 结构
	return &model.SensorData{
		SensorID:  fmt.Sprintf("%d-%s", parsed.DeviceAddr, p.GetType()), // 设备唯一ID（地址+类型）
		Type:      p.GetType(),                                          // 传感器类型：yu_liang
		Model:     p.GetModel(),                                         // 传感器型号：ZTS-YL-*-3001-*
		Timestamp: time.Now().UnixMilli(),                               // 毫秒级时间戳
		RawData:   resp,                                                 // 原始应答帧（调试用）
		Values: map[string]string{
			"rainfall": fmt.Sprintf("%.1f", parsed.Rainfall), // 雨量值（单位mm）
		},
		Protocol: p.GetProtocolName(), // 协议名称：雨量
	}, nil
}

// calculateCRC 计算ModBus-RTU CRC16校验码（严格遵循手册4.2节：多项式0xA001，低位在前）
func calculateCRC(data []byte) uint16 {
	crc := uint16(0xFFFF)
	for _, b := range data {
		crc ^= uint16(b)
		for i := 0; i < 8; i++ {
			if crc&0x0001 != 0 {
				crc >>= 1
				crc ^= 0xA001 // 手册隐含的ModBus CRC多项式（行业标准）
			} else {
				crc >>= 1
			}
		}
	}
	return crc
}
